unit uALSDAThread;

interface

uses
  SysUtils, Classes, Variants, ActiveX, DB, ADODB, Windows, WideStrings,
  uDZSysUtils, uDZThread, uDZTrace, uDZLinkList, uDZDatabase, uDZStream,
  uGxtSvcIntf;

type
  TALSDAThread = class(TDzThread)
  private
    fConnection: TADOConnection;
    fQuery: TADOQuery;
    fCachedSqlList: TWideStrings;
    function ExecSql(const command: WideString;
      needResult: Boolean = False): Boolean;
    function UpdateLocation(threshold: Integer): Boolean;
    procedure ConnectDB;
    procedure DisconnectDB;
    function ThreadLoop: Boolean;
  protected
    procedure Execute; override;
  end;

implementation

uses
  uDZStr, uAppConfig, uDefine, uAppLog, uAppCmdLineParams,
  uGxtMobileLocateNative, uCLDWProtocol, uLBMPTrackClient, uALSConst;

function TALSDAThread.ExecSql(const command: WideString;
  needResult: Boolean): Boolean;
begin
  if AppConfig.LogSQL then WriteLog(tsDebug, command);
  Result := False;
  fQuery.Close;
  fQuery.SQL.Text := command;
  while True do
  try
    if needResult then fQuery.Open
    else fQuery.ExecSQL;
    Result := True;
    Break;
  except
    on e: Exception do
    begin
      FmtWriteLog(tsError, SExecSQLError, [string(command), e.Message]);
      if CanReConnect(e.Message) then
      begin
        fConnection.Connected := False;
        try
          fConnection.Connected := True;
        except
          on E: Exception do
          begin
            LogException('ALSDAThread.ExecSql', E);
            Break;
          end;
        end;
      end
      else Break;
    end;
  end;
end;

procedure TALSDAThread.Execute;
const
  WAIT_TIMEOUT = 3 * 60 * 1000;
begin
  fConnection := TADOConnection.Create(nil);
  fConnection.LoginPrompt := False;
  fConnection.ConnectionTimeout := 10;
  fConnection.ConnectionString := AppConfig.DBParams.ConnString;
  fQuery := TADOQuery.Create(nil);
  fQuery.Connection := fConnection;
  fCachedSqlList := TWideStringList.Create;
  try
    while not Stopped do
    begin
      try
        ConnectDB;
        if fConnection.Connected and ThreadLoop then Continue;
        DisconnectDB;
        if Stopped or WaitForStopSignal(WAIT_TIMEOUT) then Break;
      except

      end;
    end;
  finally
    UpdateLocation(0);
    fCachedSqlList.Free;
    fQuery.Free;
    fConnection.Free;
    FmtWriteLog(tsInformation, 'database thread(id = %x) exit.'#10, [ThreadId]);
  end;
end;

function TALSDAThread.ThreadLoop: Boolean;
const 
  BATCH_UPDATE_COUNT = 50;
var
  cnt, i: Integer;
  mobile: TAutoLocMobile;
  fldSuiCheShouShouJi: TField;
  SQL_GET_AUTOLOC: WideString;
begin
  if CmdLineParams.DebugMode then
    SQL_GET_AUTOLOC := 'select distinct top 100 SuiCheShouJi ' +
    'from Cxt_56888_net.dbo.CarInfo ' +
      'where (ZhuCeFou = 3) and (Car_Del = 0)'
  else
    SQL_GET_AUTOLOC := 'select distinct top 100 SuiCheShouJi ' +
      'from Cxt_56888_net.dbo.CarInfo where (ZhuCeFou = 3) and ' +
      '(Car_Del = 0) and (Car_IsAutoLoc = 0) and ' +
      '(DatePart(hour, getdate()) >= AutoLocTime)';
  Result := False;
  if fCachedSqlList.Count > 0 then
  for i := fCachedSqlList.Count - 1 downto 0 do
  begin
    if Self.ExecSql(fCachedSqlList[i]) then fCachedSqlList.Delete(i)
    else Exit;
  end;

  Pending.Lock;
  try
    Completed.Lock;
    try
      if Pending.Count = 0 then
      begin
        if not ExecSql(SQL_GET_AUTOLOC, True) then
        begin
          WriteLog(tsError, SQueryLocateTargetError);
          Exit;
        end;
        if fQuery.RecordCount = 0 then
        begin
          WriteLog(tsInformation, SNoLocateTarget);
          Exit;
        end;
        fldSuiCheShouShouJi := fQuery.FieldByName('SuiCheShouJi');
        cnt := 0;
        while not fQuery.Eof do
        begin
          if GetMobileNetwork(fldSuiCheShouShouJi.AsString) =
            ctnChinaMobile then
          begin
            mobile := TAutoLocMobile(Pending.Add);
            mobile.Phone := fldSuiCheShouShouJi.AsString;
            Inc(cnt);
          end
          else begin
            mobile := TAutoLocMobile(Completed.Add);
            mobile.Phone := fldSuiCheShouShouJi.AsString;
            mobile.AreaCode := 0;
            mobile.ErrCode := GxtLBS_INVALID_OPERATION;
            mobile.AreaName := SCityLocUnsupported;
            mobile.State := 3;
            InterlockedIncrement(LocateError);
          end;
          fQuery.Next;
        end;
        if CmdLineParams.DebugMode then
        begin
          TAutoLocMobile(Pending.Add).Phone := '13724207890';
          Inc(cnt);
        end;
        FmtWriteLog(tsInformation, SGetLocateTarget, [cnt]);
        InterlockedExchangeAdd(NeedToLocate, fQuery.RecordCount);
        if cnt > 0 then with AppConfig do
          ReleaseSemaphore(hSemphore, (cnt + NumberOfTargetPerLocate - 1)
            div NumberOfTargetPerLocate, nil);
      end;
    finally
      Completed.Release;
    end;
  finally
    Pending.Release;
  end;

  Writelog(tsDebug, 'waiting for locate result'#10);
  while Locating > 0 do
  with AppConfig do
  begin
    if Completed.Count > BatchUpdateCacheSize then
    begin
      if not UpdateLocation(BatchUpdateCacheSize) then Exit;
    end
    else begin
      if WaitForStopSignal(100) then Exit;
    end;
  end;

  if not UpdateLocation(0) then Exit;

  Writelog(tsDebug, 'locate completed'#10);

  Result := True;
end;

{$WARNINGS OFF}
function TALSDAThread.UpdateLocation(threshold: Integer): Boolean;
const
  SQL_WRITE_LOCATION: WideString =
    'exec Cxt_56888_net.dbo.Car_UpdateLocation3 %d, 1, 0, 0, ' +
    '%d, ''%s'', %d, %d, ''%s'', ''%s'', '''', '''', %d'#13#10;
var
  mobile: TAutoLocMobile;
  SQLCmds: TDzWideStrStream;
  item: TCLDWItem;
  list: TCLDWItemList;
begin
  SQLCmds := TDzWideStrStream.Create('');
  try
    while Completed.Count > threshold do
    with AppConfig do
    begin
      SQLCmds.Size := SQLCommandMaxBytes;
      SQLCmds.Seek(0, soFromBeginning);
      Completed.Lock;
      if UseNewTrackServer then list := TCLDWItemList.Create;
      try
        while (Completed.Count > 0) and (SQLCmds.Position <
          SQLCommandMaxBytes - 3 * Length(SQL_WRITE_LOCATION)) do
        begin
          mobile := TAutoLocMobile(Completed.Pop);
          if UseNewTrackServer then
          begin
            item := TCLDWItem(list.Add);
            item.Phone := mobile.Phone;
            item.LocType := Ord(lbsCityLoc) + 1;
            item.AddTime := Now;
            item.CityCode := mobile.CityCode;
            item.JingDu := 0;
            item.JingDu := 0;
            item.WZMS := mobile.AreaName;
          end;
          with mobile do
            SQLCmds.FormatWriteBuf(PWideChar(SQL_WRITE_LOCATION),
              Length(SQL_WRITE_LOCATION),
              [Ord(not UseNewTrackServer), 0, WideString(Phone),
              ErrCode, CityCode, AreaName, AreaName, State]);
          mobile.Free;
        end;
        if UseNewTrackServer then
        try
          uLBMPTrackClient.SaveLBMPTrack(list);
        except

        end;
      finally
        if UseNewTrackServer then list.Free;
        Completed.Release;
      end;
      SQLCmds.Size := SQLCmds.Position;
      if not Self.ExecSql(SQLCmds.DataString) then
      begin
        fCachedSqlList.Add(SQLCmds.DataString);
        Result := False;
        Exit;
      end;
    end;
  finally
    SQLCmds.Free;
  end;
  Result := True;
end;

{$WARNINGS ON}
procedure TALSDAThread.DisconnectDB;
begin
  fConnection.Connected := False;
end;

procedure TALSDAThread.ConnectDB;
begin
  if fConnection.Connected then Exit;
  repeat
    try
      fConnection.Connected := True;
      WriteLog(tsInformation, SDBConnOK);
    except
      on E: Exception do
      begin
        FmtWriteLog(tsError, SDBConnError, [E.Message]);
        if WaitForStopSignal(60 * 1000) then Exit;
      end;
    end;
  until fConnection.Connected;
end;

end.

